/***************************************************************
 * CHT8305/CHT8310温湿度传感器驱动
 * 功能：支持多型号检测、数据采集与转换
 * 硬件依赖：STM32 HAL库、I2C外设
 ***************************************************************/
#include "cht8305_8310.h"

//---------------------------------------------------------------------------------------------------
extern I2C_HandleTypeDef hi2c2;
//---------------------------------------------------------------------------------------------------
// 硬件配置宏定义
//---------------------------------------------------------------------------------------------------
#define SENSOR_I2C_ADDR_7BIT_DEFAULT 0x40   // 默认I2C地址，根据硬件调整
#define SENSOR_MAX_DELAY             100    // 最大延时
#define SENSOR_IIC_PORT              hi2c2  // iic端口
#define CHT8305_CONVERSION_TIME_MS   30     // CHT8305测量时间（单位：ms）手册建议>=20ms
//---------------------------------------------------------------------------------------------------
// 类型和全局变量声明
//---------------------------------------------------------------------------------------------------
static sensor_ctx_t g_sensor_ctx = { 0 };
//---------------------------------------------------------------------------------------------------
// I2C底层驱动
//---------------------------------------------------------------------------------------------------
/**
 * @brief  I2C写操作（寄存器地址+数据）
 * @param  dev_addr  设备7位地址
 * @param  reg_addr  目标寄存器地址
 * @param  data      待写入数据缓冲区
 * @param  len       数据长度（字节）
 * @retval bool      true-成功 false-失败
 * @note   数据格式：[寄存器地址][数据字节1][数据字节2]...
 */
static bool i2c_write( uint8_t dev_addr_7bit, uint8_t reg_addr, uint8_t* data, uint8_t len )
{
    uint8_t buffer[ len + 1 ];
    buffer[ 0 ] = reg_addr;             // 寄存器地址作为第一个字节
    memcpy( &buffer[ 1 ], data, len );  // 数据跟在后面

    HAL_StatusTypeDef status = HAL_I2C_Master_Transmit( &SENSOR_IIC_PORT,
                                                        dev_addr_7bit << 1,  // 转换为HAL的8位地址格式
                                                        buffer, len + 1, SENSOR_MAX_DELAY );
    return ( status == HAL_OK );
}

/**
 * @brief  I2C读操作（先写寄存器地址，再读取数据）
 * @param  dev_addr  设备7位地址
 * @param  reg_addr  目标寄存器地址
 * @param  buffer    数据接收缓冲区
 * @param  len       要读取的字节数
 * @retval bool      true-成功 false-失败
 * @note   使用重复启动信号实现标准I2C读操作流程
 */
static bool i2c_read( uint8_t dev_addr_7bit, uint8_t reg_addr, uint8_t* buffer, uint8_t len )
{
    // 先发送寄存器地址（写模式）
    HAL_StatusTypeDef status =
        HAL_I2C_Master_Transmit( &SENSOR_IIC_PORT, dev_addr_7bit << 1, &reg_addr, 1, SENSOR_MAX_DELAY );
    if( status != HAL_OK )
        return false;

    // 再读取数据（读模式）
    status = HAL_I2C_Master_Receive( &SENSOR_IIC_PORT,
                                     ( dev_addr_7bit << 1 ) | 0x01,  // 添加读位
                                     buffer, len, SENSOR_MAX_DELAY );
    return ( status == HAL_OK );
}
/**
 * @brief  毫秒级延时函数
 * @param  ms  延时时间（毫秒）
 * @note   基于HAL_Delay实现，可根据需要替换为其他实现
 */
static void sensor_delay_ms( uint32_t ms )
{
    HAL_Delay( ms );
}

/**
 * @brief  带符号保护的右移位运算
 * @param  x  待移位的有符号16位整数
 * @param  n  右移位数
 * @retval int16_t 移位后的结果
 * @note   保持符号位不变，避免标准右移引入的符号扩展问题
 */
int16_t right_shift( int16_t x, int n )
{
    int16_t sign_bit  = ( x >> 15 ) & 1;                              // 获取符号位
    int16_t shifted_x = x >> n;                                       // 右移n位
    shifted_x         = ( shifted_x & 0x7FFF ) | ( sign_bit << 15 );  // 重新设置符号位
    return shifted_x;
}

//---------------------------------------------------------------------------------------------------
// 传感器型号检测
//---------------------------------------------------------------------------------------------------
/**
 * @brief  通过读取ID寄存器识别传感器型号
 * @param  i2c_addr  设备I2C地址
 * @retval sensor_model_t 检测到的传感器型号
 * @note   依赖寄存器：
 *         - 0xFE: Manufacturer ID（固定值0xA1A1）
 *         - 0xFF: Version ID（区分型号的关键）
 */
static sensor_model_t detect_sensor_model( uint8_t i2c_addr_7bit )
{
    uint16_t dev_id    = 0;
    uint16_t version   = 0;
    uint8_t  temp[ 2 ] = { 0 };

    // 读取Manufacture ID
    if( !i2c_read( i2c_addr_7bit, REG_CHT8310_MANUF_ID, temp, 2 ) )
    {
        return SENSOR_UNKNOWN;
    }
    dev_id = ( temp[ 0 ] << 8 ) | temp[ 1 ];

    // 读取Version ID
    memset( temp, 0, 2 );
    if( !i2c_read( i2c_addr_7bit, REG_CHT8310_VERSION_ID, temp, 2 ) )
    {
        return SENSOR_UNKNOWN;
    }
    version = ( temp[ 0 ] << 8 ) | temp[ 1 ];

    g_sensor_ctx.manufacture_id = dev_id;
    g_sensor_ctx.version_id     = version;

    switch( version )
    {
    case CHT8305_VERSION_ID:
        return SENSOR_CHT8305C;
    case CHT8310_VERSION_ID:
        return SENSOR_CHT8310;
    default:
        return SENSOR_UNKNOWN;
    }

    return SENSOR_UNKNOWN;
}

//---------------------------------------------------------------------------------------------------
// 传感器初始化
//---------------------------------------------------------------------------------------------------

/**
 * @brief  传感器初始化入口函数
 * @retval bool true-成功 false-失败
 * @note   执行流程：
 *         1. 检测传感器型号
 *         2. 型号特定配置
 *         3. 验证初始化状态
 */
bool cht8305_8310_sensor_init( void )
{
    g_sensor_ctx.i2c_addr_7bit = SENSOR_I2C_ADDR_7BIT_DEFAULT;

    // 检测传感器型号
    g_sensor_ctx.model = detect_sensor_model( g_sensor_ctx.i2c_addr_7bit );
    if( g_sensor_ctx.model == SENSOR_UNKNOWN )
        return false;

    // 型号特定初始化
    if( g_sensor_ctx.model == SENSOR_CHT8310 )
    {
        // CHT8310：配置转换速率为1次/秒
        uint8_t conv_rate[ 2 ] = { 0x40, 0x00 };
        // i2c_write( g_sensor_ctx.i2c_addr_7bit, 0x04, conv_rate, 2 );

        // CHT8310需读取EM模式
        uint8_t config[ 2 ];
        i2c_read( g_sensor_ctx.i2c_addr_7bit, REG_CHT8310_CONFIG, config, 2 );
        g_sensor_ctx.config_msb.value = config[ 0 ];
        g_sensor_ctx.config_lsb.value = config[ 1 ];
    }
    else
    {
        // CHT8305C：启用连续测量模式
        uint8_t config[ 2 ] = { 0x10, 0x00 };  // 默认配置
        // i2c_write( g_sensor_ctx.i2c_addr_7bit, REG_CONFIG_CHT8305C, config, 2 );
    }

    g_sensor_ctx.is_initialized = true;
    return true;
}

//---------------------------------------------------------------------------------------------------
// 数据采集与转换
//---------------------------------------------------------------------------------------------------

/**
 * @brief  读取原始温湿度数据
 * @param  temp   温度原始值输出指针
 * @param  humid  湿度原始值输出指针
 * @retval bool   true-成功 false-失败
 * @note   不同型号读取方式：
 *         - CHT8305: 触发转换后连续读取4字节（温度+湿度）
 *         - CHT8310: 分两次独立读取温湿度寄存器
 */
bool sensor_read_raw( uint16_t* temp_raw, uint16_t* humid_raw )
{
    uint8_t buffer[ 4 ];

    switch( g_sensor_ctx.model )
    {
    case SENSOR_CHT8305C:
        // 触发转换
        i2c_write( g_sensor_ctx.i2c_addr_7bit, REG_CHT8305_TEMP, buffer, 1 );

        // 等待转换完成
        sensor_delay_ms( CHT8305_CONVERSION_TIME_MS );

        // 连续读取4字节
        if( !i2c_read( g_sensor_ctx.i2c_addr_7bit, REG_CHT8305_TEMP, buffer, 4 ) )
            return false;

        *temp_raw  = ( buffer[ 0 ] << 8 ) | buffer[ 1 ];
        *humid_raw = ( buffer[ 2 ] << 8 ) | buffer[ 3 ];
        break;

    case SENSOR_CHT8310:
        // 分两次独立读取
        if( !i2c_read( g_sensor_ctx.i2c_addr_7bit, REG_CHT8310_TEMP, buffer, 2 ) )
            return false;
        *temp_raw = ( buffer[ 0 ] << 8 ) | buffer[ 1 ];

        if( !i2c_read( g_sensor_ctx.i2c_addr_7bit, REG_CHT8310_HUMI, buffer, 2 ) )
            return false;
        *humid_raw = ( buffer[ 0 ] << 8 ) | buffer[ 1 ];
        break;

    default:
        return false;  // 未知型号无法读取
    }

    return true;
}

/**
 * @brief  温度原始值转实际温度（℃）
 * @param  raw  16位原始数据
 * @retval float 实际温度值
 * @note   转换公式依据数据手册：
 *         - CHT8305: (raw/65535)*165 - 40
 *         - CHT8310: (raw >> 2) * 0.03125 （14位模式）
 */
float sensor_convert_temp( int16_t raw )
{
    switch( g_sensor_ctx.model )
    {
    case SENSOR_CHT8305C:
        return ( raw * 165.0f / 65535.0f ) - 40.0f;

    case SENSOR_CHT8310:
        if( g_sensor_ctx.config_msb.bit.EM )
            return right_shift( raw, 2 ) * 0.03125f;
        else
            return ( right_shift( raw & 0xFFF8, 3 ) ) * 0.03125f;

    default:
        return 0.0f;  // 未知型号返回默认值
    }
}

/**
 * @brief  湿度原始值转实际湿度（%RH）
 * @param  raw  16位原始数据
 * @retval float 实际湿度值
 * @note   转换公式依据数据手册：
 *         - CHT8305: (raw/65535)*100
 *         - CHT8310: (raw & 0x7FFF)/32768*100
 */
float sensor_convert_humid( uint16_t raw )
{
    switch( g_sensor_ctx.model )
    {
    case SENSOR_CHT8305C:
        return raw * 100.0f / 65535.0f;

    case SENSOR_CHT8310:
        if( raw & 0x8000 )
            return 100.0f;
        return ( raw & 0x7FFF ) * 100.0f / 32768.0f;

    default:
        return 0.0f;  // 未知型号返回默认值
    }
}
//---------------------------------------------------------------------------------------------------
// 对外接口
//---------------------------------------------------------------------------------------------------

/**
 * @brief  获取最新温湿度数据
 * @param  temp   温度输出指针（单位℃）
 * @param  humid  湿度输出指针（单位%RH）
 * @note   典型调用周期建议≥1秒（取决于传感器转换时间）
 */
void get_cht8305_8310_temp_humi( float* temp, float* humi )
{
    uint16_t temp_raw, humid_raw;
    if( !sensor_read_raw( &temp_raw, &humid_raw ) )
    {
        *temp = 0.0f;
        *humi = 0.0f;
        return;
    }

    *temp = sensor_convert_temp( temp_raw );
    *humi = sensor_convert_humid( humid_raw );
}
